package com.atguigu.gmall.activity.service.impl;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.activity.util.CacheHelper;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.MD5;
import com.atguigu.gmall.model.activity.OrderRecode;
import com.atguigu.gmall.model.activity.SeckillGoods;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author Fxz
 * @version 1.0
 * @date 2022-02-25 16:12
 */
@SuppressWarnings("all")
@RequiredArgsConstructor
@Service
public class SeckillGoodsServiceImpl implements SeckillGoodsService {

	private final RedisTemplate redisTemplate;

	private final SeckillGoodsMapper seckillGoodsMapper;

	/**
	 * 查询所有的秒杀商品
	 */
	@Override
	public List<SeckillGoods> findAll() {
		return redisTemplate.opsForHash().values(RedisConst.SECKILL_GOODS);
	}

	/**
	 * 根据商品id 查询商品数据详情
	 */
	@Override
	public SeckillGoods findSeckillGoodsById(Long skuId) {
		return (SeckillGoods) redisTemplate.opsForHash().get(RedisConst.SECKILL_GOODS, skuId.toString());
	}

	/**
	 * 监听秒杀下单消息 预下单
	 */
	@Override
	public void seckillOrder(Long skuId, String userId) {

		String state = (String) CacheHelper.get(skuId.toString());
		// 判断是否售罄
		if ("0".equals(state)) {
			return;
		}

		// 判断用户是否下过订单
		String userOrderKey = RedisConst.SECKILL_USER + userId;
		// 执行setnx命令
		Boolean flag = redisTemplate.opsForValue().setIfAbsent(userOrderKey, skuId, RedisConst.SECKILL__TIMEOUT,
				TimeUnit.SECONDS);
		// 判断用户是否已经下过订单
		if (!flag) {
			return;
		}

		// 从缓存中扣减库存
		Long increment = redisTemplate.opsForValue().increment(RedisConst.SECKILL_STOCK_PREFIX + skuId, -1);
		// 判断库存是否充足
		if (increment < 0) {
			// 通知其他节点库存不足
			redisTemplate.convertAndSend("seckillpush", skuId + ":0");
		}

		// 将OrderRecode放入缓存 说明用户拥有了下单资格
		OrderRecode orderRecode = new OrderRecode();
		orderRecode.setUserId(userId);
		orderRecode.setSeckillGoods(findSeckillGoodsById(skuId));
		orderRecode.setNum(1);
		orderRecode.setOrderStr(MD5.encrypt(skuId + userId));

		// 放入缓存
		String orderKey = RedisConst.SECKILL_ORDERS;
		redisTemplate.opsForHash().put(orderKey, userId, orderRecode);

		// 更新库存
		updateStockCount(skuId);
	}

	/**
	 * 检查秒杀状态
	 */
	@Override
	public Result checkOrder(Long skuId, String userId) {

		// 判断用户在缓存中是否存在
		Boolean flag = redisTemplate.hasKey(RedisConst.SECKILL_USER + userId);
		if (flag) {
			// 判断用户是否抢单成功(预下单)
			String orderKey = RedisConst.SECKILL_ORDERS;
			Boolean result = redisTemplate.opsForHash().hasKey(orderKey, userId);
			if (result) {
				OrderRecode orderRecode = (OrderRecode) redisTemplate.opsForHash().get(orderKey, userId);
				return Result.build(orderRecode, ResultCodeEnum.SECKILL_SUCCESS);
			}
		}

		// 判断用户是否下过订单
		String ordersUserKey = RedisConst.SECKILL_ORDERS_USERS;
		Boolean hasKey = redisTemplate.opsForHash().hasKey(ordersUserKey, userId);
		if (hasKey) {
			// 已经下过订单
			String orderId = (String) redisTemplate.opsForHash().get(ordersUserKey, userId);
			return Result.build(orderId, ResultCodeEnum.SECKILL_ORDER_SUCCESS);
		}

		// 通过状态位判断是否售罄
		String state = (String) CacheHelper.get(skuId.toString());
		if ("0".equals(state)) {
			return Result.build(null, ResultCodeEnum.SECKILL_FAIL);
		}

		// 排队中
		return Result.build(null, ResultCodeEnum.SECKILL_RUN);
	}

	/**
	 * 更新库存
	 */
	public void updateStockCount(Long skuId) {
		// 剩余库存数
		Integer count = (Integer) redisTemplate.opsForValue().get(RedisConst.SECKILL_STOCK_PREFIX + skuId);
		// 指定规则更新库存
		if (count % 2 == 0) {
			// 更新缓存
			SeckillGoods seckillGoods = findSeckillGoodsById(skuId);
			seckillGoods.setStockCount(count);
			redisTemplate.opsForHash().put(RedisConst.SECKILL_GOODS, skuId.toString(), seckillGoods);

			// 更新数据库
			seckillGoodsMapper.updateById(seckillGoods);
		}
	}

}
